<?php

/**
 * Designed to work with PayPal Website Payments Standard
 * 15 November 2008
 * Author: Anthony Gallon
 */

class ISubscribe_PayPal_Website_Payments_Standard_1 extends ISubscribe_Payment_Gateway
{
	protected $method = 'offsite'; // can be offsite or onsite
	protected $config = array();
	protected $paypalIpnPost = array();
	protected $paypalStatus = 'UNINITIALIZED';

	/**
	 * Gets called only upon instantiation of a new object, such as
	 * 	new installation or failure to unserialize the object from the database.
	 */
	public function __construct(){
		parent::__construct();

		// our configuration parameters for this particular gateway.
	    $this->config = array(

	     	// our account holder email address
	    	0 => array(
	    		// keyname to call. eg: $email = $this->getConfig('email');
	    		'key' => 'email',

	    		// Friendly name for label in config form
	    		'title' => 'PayPal account email',

	    		// actual value (gets overwritten by Config Gateway page)
	    		'value' => 'you@example.com',

	    		// field type: text, textarea, select, longtag, tinymce
	    		'type' => 'text',

	    		// options (only used for types of "select")
	    		'options' => array(
	    			'yes' => 'Yes',
	    			'no' => 'No',
	    			'maybe' => 'Perhaps'
	    		),

	    		// classname for form field in Config Gateway page
	    		'class' => 'paypal-email',

	    		// id for form field in Config Gateway page
	    		'id' => 'paypal-email',

	    		// maximum length of input string, default: 255
	    		'maxlength' => 255
	    	),

	    	// an example of a hidden configuration value. This means the user cannot alter it
	    	1 => array(
	    		'key' => 'paypal_url',
	    		'value' => 'https://www.paypal.com/cgi-bin/webscr',
	    		'hidden' => 1
	    	),

	    	// text to display on the paypal return and complete button
	    	2 => array(
	    		'key' => 'complete_button_text',
	    		'title' => 'Display name for PayPal return and complete button',
	    		'value' => 'Claim your subscription',
	    		'type' => 'text'
	    	),

			// for payment data transfer confirmations (not yet implemented)
	    	3 => array(
	    		'key' => 'pdt_token',
	    		'title' => 'PayPal PDT Token',
	    		'value' => '',
	    		'type' => 'text',
	    		'class' => 'wide',
	    		'hidden' => 1
	    	),

	    	// allow the user to create their own cancelled error message
	    	// Note: you should include this field in your gateway implementation if using an offsite method
	    	4 => array(
	    		'key' => 'cancelled_msg',
	    		'title' => 'Cancelled payment message',
	    		'value' => '<h2>Payment was cancelled!</h2>' .
	    					'<p>PayPal reported that you cancelled the transaction ' .
							'before payment was completed. Your subscription has not been ' .
							'granted.</p>',
				'type' => 'tinymce',
				'maxlength' => 5000
	    	),

	    	// redirection notice displayed when redirecting to paypal website
	    	5 => array(
	    		'key' => 'redirection_notice',
	    		'title' => 'Notice when waiting to redirect to PayPal',
	    		'value' => '<h2>Redirecting to PayPal</h2>' .
	    				'<p>Please wait a few seconds while we redirect you to the PayPal ' .
	    				'website. If you are not redirected in 5 seconds, click the button ' .
	    				'below.</p>',
	    		'type' => 'tinymce',
	    		'hidden' => 0,
	    		'maxlength' => 5000
	    	),
	    	
	    	// display the correct account name on the return link
	    	6 => array(
          'key' => 'site_name',
          'title' => 'Website Name for returning from PayPal',
          'value' => 'Acme Inc.',
          'type' => 'text',
          'maxlength' => 255
        )

	    );
	}

    public function getPaypalIpnPost(){
      return $this->paypalIpnPost;
    }

    /**
     * Returns html enabling the user to pay for the subscription in their selected currency     
     * Accepts: $prices as array, $subscription data as array, form url
     * Returns: html     
     */              
    public function getCurrencySelector($prices, $subData, $payUrl){
        
        global $gCms;
        
        $html = '';
        $html .= '<h2>Pay with PayPal - It\'s fast, easy and secure!</h2>';
        
        if(count($prices)==1){
            foreach($prices as $k=>$v) break;
            $html .= '<form method="post" action="'.$payUrl.'">';
            $html .= '<input type="hidden" name="price_id" value="'.$prices[$k]['id'].'" />';
            $html .= '<input type="submit" value="'.$prices[$k]['price'].' '.$prices[$k]['currency_key'].'" />';
            $html .= '</form>';
        }else{
            $paymentOpts = array();
            foreach($prices as $k=>$price){
                $paymentOpts[$price['id']] = $price['price'].' '.$price['currency_key'];
            };
            $sel = array(
                'name' => 'price_id',
                'type' => 'select',
                'value' => post('price_id'),
                'label' => 'Currencies',
                'options' => $paymentOpts,
                'showLabel' => false,
                'setWrap' => false
            );
            $selObj = Antz::makeFormComponent($sel);
            $html .= '<form method="post" action="'.$payUrl.'">';
            $html .= $selObj->render();
            $html .= '<input type="submit" value="'.$gCms->modules['ISubscribe']['object']->Lang('submit').'" />';
            $html .= '</form>';
        }
        return $html;
    }


	/**
	 * Accepts array in identical format as $this->config, but with values updated by /actions/admin.config-gateway.php
	 * Must implement some logic to check for clean input
	 * Must call parent::setConfig($config) if no errors detected
	 * Report errors in this format:
	 * 	$this->errors[] = 'You have a spelling mistake in '.$config[$i]['key'];
	 * 	$this->formErrors[$config[$i]['key']] = 'Spelling mistake in this field';
	 * Return true or false
	 */
	public function setConfig($config){

/*
		echo '<pre>';
		var_dump($config);
		echo '</pre>';
*/

		// lets check our data to make sure it is clean
		$error = false;

		foreach($config as $k=>$conf){
			switch($conf['key']){
				case 'email':
					if(!Antz_Filter::checkEmail($conf['value'])){
						$this->errors[] = 'Invalid email format: '.$conf['value'];
						$this->formErrors[$conf['key']] = 'Invalid email format';
						$error = true;
					};
				break;
				case 'cancelled_msg':
					if(trim($conf['value'])==''){
						$error = true;
						$this->errors[] = 'Cancelled message cannot be blank';
						$this->formErrors[$conf['key']] = 'Cannot be blank';
					};
				break;
				case 'complete_button_text':
					if(trim($conf['value'])==''){
						$error = true;
						$this->errors[] = 'Completed button text cannot be blank';
						$this->formErrors[$conf['key']] = 'Cannot be blank';
					};
				break;
				case 'redirection_notice':
					if(trim($conf['value'])==''){
						$error = true;
						$this->errors[] = 'Redirection notice cannot be blank';
					};
				break;
				default:
					// other field types are ok to accept as-is, since they have had non-whitelist tags
					// stripped by Antz_Input::getPostValue() and truncated to $conf['maxlength']
			};
		};

		// return false if we found an error, this will prevent unclean data getting saved
		if($error) return false;

		// getting here means our data is clean and can be saved.
		parent::setConfig($config);

		return true;
	}

	/**
	 * Accepts an array in the format of one row from the table cms_isubscribe_subscription_prices
	 */
	public function setPriceData($priceData){
		parent::setPriceData($priceData);
	}

	/**
	 * Accepts an array in the format of one row from the table cms_isubscribe_subscription
	 */
	public function setSubscriptionData($subData){
		parent::setSubscriptionData($subData);
	}

	/**
	 * Accepts: $completeReturnUrl, $cancelReturnUrl, $ipnUrl
	 * Returns string containing form and button to direct the user to the offsite gateway processor
	 * May use Antz::redir($url) to redirect immediately instead of returning a form, depending on the gateway
	 */
	public function getButton($completeReturnUrl, $cancelReturnUrl, $ipnUrl){
    $USER = Antz::registry('user');

		$siteName = $this->getConfig('site_name');
		$email = $this->getConfig('email');
		$cbt = $this->getConfig('complete_button_text');
		$paypalUrl = $this->getConfig('paypal_url');
		$redirectionNotice = $this->getConfig('redirection_notice');

		$btn = <<<BTN
{$redirectionNotice}
<form action="{$paypalUrl}" method="post" id="paypal-button-form">
   <input type="hidden" name="cmd" value="_xclick" />
   <input type="hidden" name="business" value="{$email}" />
   <input type="hidden" name="item_name" value="{$this->subscriptionData['title']}" />
   <input type="hidden" name="item_number" value="{$this->subscriptionData['keyname']}" />
   <input type="hidden" name="amount" value="{$this->priceData['price']}" />
   <input type="hidden" name="no_shipping" value="2" />
   <input type="hidden" name="no_note" value="1" />
   <input type="hidden" name="currency_code" value="{$this->priceData['currency_key']}" />
   <input type="hidden" name="bn" value="IC_Sample" />
   <input type="hidden" name="cancel_return" value="{$cancelReturnUrl}" />
   <input type="hidden" name="return" value="{$completeReturnUrl}" />
   <input type="hidden" name="notify_url" value="{$ipnUrl}" />
   <input type="hidden" name="cbt" value="{$cbt}" />
   <input type="hidden" name="lc" value="{$USER->lang}" />
   <input type="image" src="https://www.paypal.com/en_US/i/btn/x-click-but23.gif"
   name="submit" alt="Make payments with payPal - it's fast, free and secure!" />
   <img alt="" src="https://www.paypal.com/en_US/i/scr/pixel.gif" width="1" height="1" />
</form>
<script type="text/javascript">
function IS_PWPS_redirect(){
	document.getElementById('paypal-button-form').submit()
}
setTimeout('IS_PWPS_redirect()', '3000');
</script>
BTN;
		return $btn;

            
            

	} // end function getButton()



   
	/**
	 * Called when an IPN request comes in that is identified as belonging to this instance
	 * This method must analyze $_GET and $_POST variables and update its state
	 * Must return and not exit, no return value required
	 */
	public function processIpn(){

		// following code adapted from PayPal example code generator

		// read the post from PayPal system and add 'cmd'
		$req = 'cmd=_notify-validate';
		foreach ($_POST as $key => $value) {
			$value = urlencode(stripslashes($value));
			$req .= "&$key=$value";
		};
		// post back to PayPal system to validate
		$header = "POST /cgi-bin/webscr HTTP/1.0\r\n";
		$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
		$header .= "Content-Length: " . strlen($req) . "\r\n\r\n";

		$fp = fsockopen ('ssl://www.paypal.com', 443, $errno, $errstr, 30);

		if (!$fp) {
			// HTTP ERROR
		} else {
			// send the verification request
			fputs ($fp, $header . $req);

			// read the response
			while (!feof($fp)) {
				$res = fgets ($fp, 1024);
				if (strcmp ($res, "VERIFIED") == 0) {
					// we had a valid response from PayPal!
					$this->paypalIpnPost = $_POST;
					$this->paypalStatus = 'VERIFIED';
					return;
				}else{
					// invalid IPN, investigate it!
					$this->paypalIpnPost = array();
					$this->paypalStatus = 'INVALID';
				};
			};
		};

	}

	/**
	 * Take any steps necessary to cancel the offsite payment.
	 * Return a message to display to the user
	 */
	public function cancelOffsitePayment(){
		// no further action is necessary for this gateway.
		$returnString = $this->getConfig('cancelled_msg');
		return $returnString;
	}

	/**
	 * Perform checks to discover if the payment was successful. $this->processIpn() might be
	 * 	useful depending on the nature of the gateway
	 * Return true or false to indicate if the payment was successful
	 */
	public function checkOffsitePayment(){

		// while testing on a non internet-accessible server
		//return true;
    
    // first up, check the status (becomes 'VERIFIED' if IPN was successful)
		if($this->paypalStatus != 'VERIFIED'){
			$this->errors[] = 'PayPal reported an '.$this->paypalStatus.' status for this transaction';
			return false;
		};

		// this is also set up by successful IPN
		if(count($this->paypalIpnPost)==0){
			// there was an error, the IPN from paypal was not received
			$this->errors[] = 'No IPN received from PayPal';
			return false;
		};
		$ipnPost = $this->paypalIpnPost;

		$status = strtolower($ipnPost['payment_status']);
		if($status == 'completed'){
			// successful payment!
			return true;
		}else{
			// unsuccessful payment, lets see why
			switch($status){
				case 'pending':
					$this->errors[] = 'Paypal returned a status of "Pending" for the reason of: '.$ipnPost['pending_reason'];
				break;
				default:
					$this->errors[] = 'Paypal returned a status of '.$ipnPost['payment_status'];
			};
			return false;
		};

		// not gonna get here, but why not return false for good measure :)
		return false;

	}

	/**
	 * Implemented for onsite payments only
	 * Perform the transaction and assess if it was successful
	 * Return true or false to indicate if the payment was successful
	 */
	public function checkOnsitePayment(){
		// programmers must override this method to provide gateway specific logic
	}

}
